package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * Die Schnittstelle zur Datenbank.
 * 
 * @author Jonas Gutermuth, David Caroli
 */
public class DatenbankSchnittstelle {

	/** Die Instanz der Datenbankschnittstelle. */
	private static DatenbankSchnittstelle datenbankSchnittstelle;
	
	/** Connection */
	private Connection conn = null;

	/**
	 * Konstruktor
	 * 
	 * @throws ClassNotFoundException
	 *             Klasse nicht gefunden.
	 * @throws SQLException
	 *             SQL-Exception
	 */
	private DatenbankSchnittstelle() throws ClassNotFoundException, SQLException {
		verbindeDB();
	}
	/**
	 * Stellt die Verbindung zur MySQL Datenbank her.
	 * 
	 * @throws ClassNotFoundException
	 *             Klasse nicht gefunden.
	 * @throws SQLException
	 *             SQL-Exception
	 */
	private void verbindeDB() throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.jdbc.Driver");
		conn = DriverManager.getConnection(helper.Zugangsdaten.DBWEBSEITE, helper.Zugangsdaten.DBBENUTZER, helper.Zugangsdaten.DBPASSWORT);
		System.out.println("Datenbank gestartet.");
	}

	/**
	 * Liefert dem Repository Stichtage.
	 * 
	 * @return die Stichtage
	 * @throws SQLException
	 *             SQL-Exception
	 * @throws ClassNotFoundException 
	 */
	Stichtag[] getStichtag() throws SQLException, ClassNotFoundException {
		ResultSet res = selectSort("stichtag", "stichtag");
		res.last();
		Stichtag stichtag[] = new Stichtag[res.getRow()];
		res.beforeFirst();
		for (int i = 0; res.next(); i++) {
			Stichtag temp = new Stichtag();
			temp.setId(res.getInt("id"));
			temp.setStichtag(res.getLong("stichtag"));
			temp.setStatus(res.getInt("status"));
			stichtag[i] = temp;
		}
		return stichtag;
	}

	/**
	 * Liefert dem Repository Gruppen.
	 * 
	 * @return die Gruppen
	 * @throws SQLException
	 *             SQL-Exception
	 * @throws ClassNotFoundException 
	 */
	Gruppe[] getGruppen() throws SQLException, ClassNotFoundException {
		ResultSet res = select("gruppe");
		res.last();
		Gruppe gruppen[] = new Gruppe[res.getRow()];
		res.beforeFirst();
		for (int i = 0; res.next(); i++) {
			Gruppe temp = new Gruppe();
			temp.setId(res.getInt("id"));
			temp.setName(res.getString("name"));
			temp.setBenutzerIds(res.getString("benutzerIds"));
			gruppen[i] = temp;
		}
		return gruppen;
	}

	/**
	 * Liefert dem Repository Ebenen.
	 * 
	 * @return die Ebenen
	 * @throws SQLException
	 *             SQL-Exception
	 * @throws ClassNotFoundException
	 *             Klasse nicht gefunden
	 */
	Ebene[] getEbenen() throws SQLException, ClassNotFoundException {
		ResultSet res = select("ebene");
		res.last();
		Ebene ebene[] = new Ebene[res.getRow()];
		res.beforeFirst();
		for (int i = 0; res.next(); i++) {
			Ebene temp = new Ebene( res.getInt("id"),
									res.getString("name"),
									res.getInt("prosaId"), res.getLong("erstellungszeit"),
									res.getInt("redaktionszustand"),
									res.getString("verantwortlichIds"),
									res.getString("redakteurIds"),
									res.getString("unterelementIds"), res.getString("feldIds"));
			ebene[i] = temp;
		}
		return ebene;
	}

	/**
	 * Liefert dem Repository Benutzer.
	 * 
	 * @return die Benutzer
	 * @throws SQLException
	 *             SQL exception
	 * @throws ClassNotFoundException 
	 */
	Benutzer[] getBenutzer() throws SQLException, ClassNotFoundException {
		ResultSet res = select("benutzer");
		res.last();
		Benutzer benutzer[] = new Benutzer[res.getRow()];
		res.beforeFirst();
		for (int i = 0; res.next(); i++) {
			Benutzer temp = new Benutzer();
			temp.setId(res.getInt("id"));
			temp.setVorname(res.getString("vorname"));
			temp.setNachname(res.getString("nachname"));
			temp.setEmail(res.getString("email"));
			temp.setPasswort(res.getString("passwort")); 
			benutzer[i] = temp;
		}
		return benutzer;
	}

	/**
	 * Liefert dem Repository Felder.
	 * 
	 * @return die Felder
	 * @throws SQLException
	 *             SQL Exception
	 * @throws ClassNotFoundException 
	 */
	Feld[] getFelder() throws SQLException, ClassNotFoundException {

		ResultSet res = select("feld");
		res.last();
		Feld felder[] = new Feld[res.getRow()];
		res.beforeFirst();
		for (int i = 0; res.next(); i++) {
			boolean dez2relevant;
			if (res.getInt("dez2relevant") == 1) {
				dez2relevant = (true);
			} else {
				dez2relevant = (false);
			}
			Feld temp = new Feld(res.getInt("id"), res.getInt("typ"), res.getString("name"), dez2relevant, res.getLong("aenderung"), res.getString("wert"));
			felder[i] = temp;
		}
		return felder;
	}

	/**
	 * Updatet einen Datendatz.
	 * 
	 * @param from
	 *            die Datenbank-Tabelle
	 * @param where
	 *            die UPDATE-Bedingung(en)
	 * @param update
	 *            UPDATE-Array
	 * @throws SQLException
	 *             SQL-Exception
	 * @throws ClassNotFoundException 
	 */
	void update(String from, String where, String[][] update) throws SQLException, IllegalArgumentException, ClassNotFoundException {
		if (update.length != 2) {
			throw new IllegalArgumentException("update.length != 2");
		}
		String sql = "UPDATE " + from + " SET ";
		for (int i = 0; i < update[0].length; i++) {
			sql += update[0][i] + "=\"" + update[1][i] + "\", ";
		}
		sql = sql.substring(0, sql.length() - 2);
		sql += " WHERE " + where;
		System.out.println(sql);
		if(conn.isClosed()) {
			verbindeDB();
		}
		Statement query = conn.createStatement();
		query.executeUpdate(sql);
	}

	/**
	 * Fügt Datensätze zur Datenbank hinzu.
	 * 
	 * @param from
	 *            die Datenbank-Tabelle
	 * @param insert
	 *            die Daten für die zu speichernden Datensätze
	 * @return generierte ID des Eingetragenen Elements oder -1, wenn die ID
	 *         nicht generiert wurde (z.B. wenn man sie fest in
	 *         "String[][] insert" übergeben hat)
	 * @throws SQLException
	 *             SQL Exception
	 * @throws ClassNotFoundException 
	 */
	int insert(String from, String[][] insert) throws SQLException, ClassNotFoundException {
		String sql = "INSERT INTO " + from + " (`";
		for (int i = 0; i < insert[0].length; i++) {
			sql += insert[0][i] + "`,`";
		}
		sql = sql.substring(0, sql.length() - 2);
		sql += ") VALUES ('";
		for (int j = 1; j < insert.length; j++) {
			for (int i = 0; i < insert[j].length; i++) {
				sql += insert[j][i] + "','";
			}
			sql = sql.substring(0, sql.length() - 2);
			sql += "),('";
		}

		sql = sql.substring(0, sql.length() - 3);

		System.out.println(sql);
		if(conn.isClosed()) {
			verbindeDB();
		}
		// Rückgabe generierter IDs
		Statement query = conn.createStatement();
		query.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
		ResultSet rs = query.getGeneratedKeys();
		if (rs.next()) {
			try {
				return rs.getInt(1);
			} catch (SQLException e) {
				if (e.getMessage().indexOf("Column not found") >= 0) {
					return -1;
				} else {
					throw e;
				}
			}
		} else {
			return -1;
		}
	}

	/**
	 * Führt eine Select-Anfrage aus.
	 * 
	 * @param from
	 *            die Datenbanktabelle
	 * @param where
	 *            die Auswahlkriterien
	 * @return das SELECT-Ergebnis
	 * @throws SQLException
	 *             the sQL exception
	 * @throws ClassNotFoundException 
	 */
	ResultSet select(String from, String where) throws SQLException, ClassNotFoundException {
		if(conn.isClosed()) {
			verbindeDB();
		}
		Statement query = conn.createStatement();
		String sql = "SELECT * FROM " + from + " WHERE " + where;
		return query.executeQuery(sql);
	}

	/**
	 * Führt eine Select-Anfrage aus.
	 * 
	 * @param from
	 *            die Datenbanktabelle
	 * @return das Ergebnis der SELECT-query
	 * @throws SQLException
	 *             SQL-Exception
	 * @throws ClassNotFoundException 
	 */
	ResultSet select(String from) throws SQLException, ClassNotFoundException {
		if(conn.isClosed()) {
			verbindeDB();
		}
		Statement query = conn.createStatement();
		String sql = "SELECT * FROM " + from;
		return query.executeQuery(sql);
	}
	
	/**
	 * Führt eine Select-Anfrage mit Order By aus.
	 * 
	 * @param from
	 *            die Datenbanktabelle
	 * @param sort
	 *            die Sortierung
	 * @return das Ergebnis der SELECT-query
	 * @throws SQLException
	 *             SQL-Exception
	 * @throws ClassNotFoundException 
	 */
	ResultSet selectSort(String from, String sort) throws SQLException, ClassNotFoundException {
		if(conn.isClosed()) {
			verbindeDB();
		}
		Statement query = conn.createStatement();
		String sql = "SELECT * FROM " + from + " ORDER BY "+sort;
		return query.executeQuery(sql);
	}

	/**
	 * Löscht einen Datensatz aus der Datenbank.
	 * 
	 * @param from
	 *            die Datenbanktabelle
	 * @param where
	 *            die Löschbedingung
	 * @throws SQLException
	 *             SQL-Exception
	 * @throws ClassNotFoundException 
	 */
	void del(String from, String where) throws SQLException, ClassNotFoundException {
		if(conn.isClosed()) {
			verbindeDB();
		}
		Statement query = conn.createStatement();
		String sql = "DELETE FROM " + from + " WHERE " + where;
		System.out.println(sql);
		query.executeUpdate(sql);
	}


	/**
	 * Liefert die (einmalig existierende) Instanz der DatenbankSchnittstelle
	 * zurück.
	 * 
	 * @return Instanz der DatenbankSchnittstelle
	 * @throws ClassNotFoundException
	 *             Klasse nicht gefunden
	 * @throws SQLException
	 *             SQL-Exception
	 */
	static DatenbankSchnittstelle getInstance() throws ClassNotFoundException, SQLException {
		if (datenbankSchnittstelle == null) {
			datenbankSchnittstelle = new DatenbankSchnittstelle();
		}
		return datenbankSchnittstelle;
	}

}
